<template>
  <div
    ref="containerRef"
    class="_animation-container"
    :class="{ _mobile: props.isMobile }"
  >
    <div class="_select-bg">
      <img :src="boxActiveArrow" />
      <img style="transform: rotate(180deg)" :src="boxActiveArrow" />
      <img
        v-if="!baseAnimationInfo.isAnimation"
        class="_select-bg-img"
        :src="BoxBg"
      />
    </div>

    <div class="_animation-wrapper">
      <ul
        ref="ulRef"
        class="_animation-list"
        :style="{ transform: `translateX(${baseAnimationInfo.currentX}px)` }"
      >
        <li
          class="_animation-item"
          :class="{ _active: tickIndex === idx }"
          v-for="(item, idx) in productList"
          :key="idx"
        >
          <div class="_image-wrapper">
            <img :src="item.product.image" />
          </div>
          <div
            class="_product-info"
            :class="{
              _show:
                idx === baseAnimationInfo.selectIndex &&
                !baseAnimationInfo.isAnimation,
            }"
          >
            <div class="_product-name">
              {{ item.product?.name }}
            </div>
            <div class="_product-price">
              <span>$</span>
              <span>{{ item.product?.amount }}</span>
            </div>
          </div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script lang="tsx" setup>
import boxActiveArrow from "@/assets/box-active-arrow.png";
import BoxBg from "@/assets/box-bg.png";
import { cubicBezier } from "@/utils/cubicBezier";

import type { HitProductListExpose } from "./hitProductList";
import { hitProductListEmits, hitProductListProps } from "./hitProductList";

const props = defineProps(hitProductListProps);
const emits = defineEmits(hitProductListEmits);

const containerRef = ref<HTMLDivElement>();
const ulRef = ref<HTMLUListElement>();
const baseAnimationInfo = reactive({
  containerWidth: 0,
  productItemWidth: 0,
  currentX: 0,
  targetX: 0,
  selectIndex: 0,
  time: 4,
  backTime: 0.3,
  excessiveTime: 0.5,
  excessiveDistance: 20,
  startTime: new Date().getTime(),
  isAnimation: false,
  animationId: 0,
  diff: 0,
  cubicBezier: cubicBezier(0.24, 0.89, 0.0, 1),
  endCallback: null,
});
const tickIndex = ref(-1);

const hitProducts = ref<API.BOX.BoxProduceItem[]>([]);
const productList = computed(() => {
  const products = [...(props.products || []), ...hitProducts.value];
  const result = products.map((item, idx: number) => {
    const middle = 0.5 * baseAnimationInfo.productItemWidth;
    const middleLeft = idx * baseAnimationInfo.productItemWidth + middle;
    return {
      product: item,
      middleLeft: middleLeft,
      section: [middleLeft - middle, middleLeft + middle],
    };
  });
  return result;
});

// 获取初始化信息
async function getInitInfo() {
  if (props.products.length > 0 && containerRef.value) {
    await new Promise((resolve) => setTimeout(resolve, 300));
    const list = containerRef.value.querySelector("._animation-list");
    const listItem = list.children?.[0] as HTMLLIElement;
    const contanerWidth = containerRef.value.getBoundingClientRect().width;
    const productItemWidth = listItem.getBoundingClientRect().width;
    baseAnimationInfo.containerWidth = contanerWidth;
    baseAnimationInfo.productItemWidth = productItemWidth;
    // 寻找当前选中的
    for (let selectIdx = 0; selectIdx < productList.value.length; selectIdx++) {
      if (productList.value[selectIdx].middleLeft > contanerWidth / 2) {
        baseAnimationInfo.selectIndex = selectIdx;
        tickIndex.value = selectIdx;
        baseAnimationInfo.currentX =
          contanerWidth / 2 - productList.value[selectIdx].middleLeft;
        break;
      }
    }
  }
}

function animationStart() {
  tickIndex.value = -1;
  emits("start");
  const base = Math.random() > 0.5 ? 1 : -1;
  const diffInstance = Math.max(
    Math.random() * (baseAnimationInfo.productItemWidth / 2),
    80
  );
  baseAnimationInfo.diff = Math.ceil(diffInstance * base);
  baseAnimationInfo.startTime = performance.now();
  baseAnimationInfo.isAnimation = true;
  baseAnimationInfo.animationId = window.requestAnimationFrame(animationRender);
}

function animationEnd() {
  baseAnimationInfo.isAnimation = false;
  window.cancelAnimationFrame(baseAnimationInfo.animationId);
  emits("end");
  baseAnimationInfo.endCallback?.();
}

async function animationRender() {
  if (baseAnimationInfo.isAnimation) {
    const diffTime = performance.now() - baseAnimationInfo.startTime;
    const time = diffTime / baseAnimationInfo.time / 1000;
    const target = baseAnimationInfo.targetX + baseAnimationInfo.diff;
    if (time < 1) {
      baseAnimationInfo.currentX =
        baseAnimationInfo.cubicBezier(time) * (target / 2) + target / 2;
      onTick(baseAnimationInfo.currentX);
      window.requestAnimationFrame(animationRender);
    } else {
      baseAnimationInfo.currentX = target;
      baseAnimationInfo.startTime = performance.now();
      animationEndRender();
    }
  }
}

function animationEndRender() {
  const diffTime = performance.now() - baseAnimationInfo.startTime;
  const endTime = diffTime / (baseAnimationInfo.backTime * 1000);
  if (endTime < 1) {
    baseAnimationInfo.currentX =
      baseAnimationInfo.targetX +
      baseAnimationInfo.diff * (1 - endTime < 0 ? 0 : 1 - endTime);
    window.requestAnimationFrame(animationEndRender);
  } else {
    baseAnimationInfo.currentX = baseAnimationInfo.targetX;
    animationEnd();
  }
}

function onTick(current: number) {
  window.setTimeout(() => {
    current = Math.abs(current) + baseAnimationInfo.containerWidth / 2;
    for (let idx = 0; idx < productList.value.length; idx++) {
      const section = productList.value[idx].section;
      if (current >= section[0] && current < section[1]) {
        if (tickIndex.value !== idx) {
          tickIndex.value = idx;
          emits("tick", idx);
        }
        break;
      }
    }
  });
}

onMounted(() => {
  getInitInfo();
});

defineExpose<HitProductListExpose>({
  startSpin: async (products, selectIdx, callback) => {
    baseAnimationInfo.endCallback = callback;
    hitProducts.value = products || [];
    baseAnimationInfo.selectIndex = selectIdx + props.products.length;
    baseAnimationInfo.targetX =
      baseAnimationInfo.containerWidth * 0.5 -
      productList.value[baseAnimationInfo.selectIndex].middleLeft;
    // animationStart();
    nextTick(() => {
      animationStart();
    });
  },
});
</script>

<style lang="less" scoped>
._animation-container {
  @apply relative w-full h-full;
  ._select-bg {
    @apply absolute flex flex-col items-center justify-between h-full;
    z-index: 0;
    padding: 0.31rem 0;
    left: 50%;
    aspect-ratio: 1 / 1;
    min-height: 0;
    transform: translateX(-50%);
    img {
      width: 1.16rem;
    }

    ._select-bg-img {
      @apply absolute top-0 left-0 bottom-0 right-0 w-full h-full;
      object-fit: contain;
    }
  }

  ._animation-wrapper {
    @apply flex items-center w-full h-full overflow-hidden;
    ._animation-list {
      @apply relative flex items-center h-full list-none p-0 m-0;
      z-index: 1;
      will-change: transform;
      ._animation-item {
        @apply flex flex-col items-center justify-center h-full overflow-hidden;
        gap: 0.3rem;
        padding: 1.69rem;
        aspect-ratio: 1 / 1;
        min-height: 0;
        ._image-wrapper {
          @apply flex-1 h-0;
          img {
            @apply h-full w-full;
            object-fit: contain;
            transform: scale(0.75);
          }
        }

        ._product-info {
          @apply flex flex-col w-full;
          gap: 0.3rem;
          visibility: hidden;
          font-size: 0.88rem;
          &._show {
            visibility: visible;
          }
          ._product-name {
            text-align: center;
            width: 100%;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
          }
          ._product-price {
            @apply flex items-center justify-center;
            gap: 0.2rem;
          }
        }

        &._active {
          ._image-wrapper {
            img {
              transform: scale(1);
            }
          }
        }
      }
    }
  }

  &._mobile {
    ._animation-wrapper {
      ._animation-list {
        ._animation-item {
          height: 100%;
          width: 10.6rem !important;
          aspect-ratio: unset;
          min-height: unset;
        }
      }
    }
  }
}
</style>
